home *** CD-ROM | disk | FTP | other *** search
/ Aminet 43 / Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso / Aminet / comm / tcp / Amster-source.lha / Amster_Install / Source / download.c < prev    next >
C/C++ Source or Header  |  2001-03-14  |  28KB  |  1,074 lines

  1. /*
  2. ** Amster - Download
  3. ** Copyright © 1999-2000 by Gürer Özen
  4. ** Copyright © 2000-2001 by Jacob Laursen
  5. **
  6. ** This program is free software; you can redistribute it and/or modify
  7. ** it under the terms of the GNU General Public License as published by
  8. ** the Free Software Foundation; either version 2 of the License, or
  9. ** (at your option) any later version.
  10. **
  11. ** This program is distributed in the hope that it will be useful,
  12. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. ** GNU General Public License for more details.
  15. **
  16. ** You should have received a copy of the GNU General Public License
  17. ** along with this program; if not, write to the Free Software
  18. ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. */
  20.  
  21. #include "amster.h"
  22.  
  23. #include <proto/dos.h>
  24. #include <proto/asl.h>
  25. #include <exec/memory.h>
  26. #include <libraries/dos.h>
  27. #include <libraries/asl.h>
  28.  
  29. #include "network.h"
  30.  
  31. #include <MUI/NListview_mcc.h>
  32. #include <MUI/NFloattext_mcc.h>
  33. #include "md5.h"
  34. #include "amster_Cat.h"
  35.  
  36.  
  37. int dl_count = 0;
  38. int QueueCount = 0;
  39. int WaitingCount = 0;
  40.  
  41. /* Private functions */
  42.  
  43. ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg);
  44. void dl_startq(struct TransferData *data, char *title, char *user, u_long ip, int port);
  45. void dl_startqi(struct TransferData *data, long key);
  46. void dl_checkqueue(struct TransferData *data);
  47. void DownloadRetry(struct TransferData *data, Object *obj, char *title, char *user, int limit);
  48. void PollWaiting(struct TransferData *data, Object *obj);
  49. int dl_filename(songtrans sd);
  50. char *chkmd5_fromlock(BPTR lock);
  51. int dl_askfname(char *fname);
  52. void dl_handlemsg(thread t, int com, APTR data);
  53. void dl_resume(struct TransferData *data);
  54. void dl_cps(struct TransferData *data);
  55. THREAD_DECL(dl_sucker);
  56.  
  57.  
  58. MUI_DISPATCH(dl_dispatch)
  59. {
  60.     struct TransferData *data;
  61.  
  62.     switch(msg->MethodID) {
  63.         case OM_NEW:
  64.             return(dl_new(cl, obj, (APTR)msg));
  65.         case MUIM_Window_Setup:
  66.             return(dl_setup(cl, obj, (APTR)msg));
  67.         case MUIM_Window_Cleanup:
  68.             return(dl_muicleanup(cl, obj, (APTR)msg));
  69.         case DL_ADD:
  70.             data = INST_DATA(cl, obj);
  71.             DoMethod(data->list, MUIM_NList_InsertSingle, (songtrans)(((muimsg)msg)->arg1), MUIV_NList_Insert_Bottom);
  72.             return(NULL);
  73.         case DL_START:
  74.             data = INST_DATA(cl, obj);
  75.             dl_startq(data, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (u_long)(((muimsg)msg)->arg3), (int)(((muimsg)msg)->arg4));
  76.             return(NULL);
  77.         case DL_STARTINCOMING:
  78.             data = INST_DATA(cl, obj);
  79.             dl_startqi(data, (long)(((muimsg)msg)->arg1));
  80.             return(NULL);
  81.         case DL_UPDATE:
  82.             {
  83.             long pos = MUIV_NList_GetPos_Start;
  84.             data = INST_DATA(cl, obj);
  85.             DoMethod(data->list, MUIM_NList_GetPos, (((muimsg)msg)->arg1), &pos);
  86.             DoMethod(data->list, MUIM_NList_Redraw, pos);
  87.             return(NULL);
  88.             }
  89.         case DL_CPS:
  90.             data = INST_DATA(cl, obj);
  91.             dl_cps(data);
  92.             return(NULL);
  93.         case DL_CLEANUP:
  94.             data = INST_DATA(cl, obj);
  95.             TransferCleanup(data);
  96.             return(NULL);
  97.         case DL_CLEANUP_SINGLE:
  98.             data = INST_DATA(cl, obj);
  99.             TransferCleanupSingle(data, (songtrans)(((muimsg)msg)->arg1));
  100.             return NULL;
  101.         case DL_ABORT:
  102.             data = INST_DATA(cl, obj);
  103.             TransferAbort(data);
  104.             return(NULL);
  105.         case DL_RESUME:
  106.             data = INST_DATA(cl, obj);
  107.             dl_resume(data);
  108.             return(NULL);
  109.         case DL_INFO:
  110.             data = INST_DATA(cl, obj);
  111.             TransferInfo(data);
  112.             return(NULL);
  113.         case DL_RESUME_UPDATE:
  114.             {
  115.             songtrans st;
  116.             data = INST_DATA(cl, obj);
  117.  
  118.             DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &st);
  119.             if (st) {
  120.                 if (st->state >= DLS_ABORT) set(data->BT_Resume, MUIA_Disabled, FALSE);
  121.             }
  122.  
  123.             return(NULL);
  124.             }
  125.         case DL_PLAY:
  126.             {
  127.             songtrans st;
  128.             data = INST_DATA(cl, obj);
  129.  
  130.             DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &st);
  131.             if (!st) return(NULL);
  132.             if (st->state < DLS_DOWN || !(st->fname)) return(NULL);
  133.             prf_event((enum Event)(((muimsg)msg)->arg1), st->fname);
  134.  
  135.             return(NULL);
  136.             }
  137.         case DL_SETERROR:
  138.             data = INST_DATA(cl, obj);
  139.             TransferSetError(data, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (int)(((muimsg)msg)->arg3));
  140.             return(NULL);
  141.         case DL_RETRY:
  142.             data = INST_DATA(cl, obj);
  143.             DownloadRetry(data, obj, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (int)(((muimsg)msg)->arg3));
  144.             return(NULL);
  145.         case DL_POLLWAIT:
  146.             data = INST_DATA(cl, obj);
  147.             PollWaiting(data, obj);
  148.             return(NULL);
  149.         case DL_REMWAITING:
  150.             data = INST_DATA(cl, obj);
  151.             if (((songtrans)(((muimsg)msg)->arg1))->state == DLS_WAIT) {
  152.                 if (--WaitingCount == 0) DoMethod(_app(obj), MUIM_Application_RemInputHandler, &data->waitnode);
  153.                 /* This was the last waiting entry - remove input handler */
  154. #ifdef AMSTER_DEBUG
  155. gui_debugf("removed waiting - dl_count: %d, QueueCount: %d, waiting: %d", dl_count, QueueCount, WaitingCount);
  156. #endif
  157.             }
  158.             return(NULL);
  159.         case DL_SETDELAY:
  160.             data = INST_DATA(cl, obj);
  161.             data->waitnode.ihn_Millis = (int)(((muimsg)msg)->arg1)*1000;
  162.             return(NULL);
  163.         case DL_WATCHER:
  164.             data = INST_DATA(cl, obj);
  165.             TransferWatcher(data);
  166.             return(NULL);
  167.         case DL_COUNTDECREMENT:
  168.             data = INST_DATA(cl, obj);
  169.             if (--dl_count == 0) DoMethod(_app(obj), MUIM_Application_RemInputHandler, &data->watchnode);
  170.             dl_checkqueue(data);
  171.             return(NULL);
  172.         case DL_COUNTINCREMENT:
  173.             data = INST_DATA(cl, obj);
  174.             if (dl_count++ == 0) DoMethod(_app(obj), MUIM_Application_AddInputHandler, &data->watchnode);
  175.             return(NULL);
  176.         case DL_CHECKQUEUE:
  177.             data = INST_DATA(cl, obj);
  178.             dl_checkqueue(data);
  179.             return(NULL);
  180.     }
  181.     return(DoSuperMethodA(cl, obj, msg));
  182. }
  183.  
  184.  
  185. ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg)
  186. {
  187.     static struct Hook downlistdispHook = { {0,0}, &translistdisp, NULL, NULL };
  188.     struct TransferData *data;
  189.     Object *list, *info, *playbut, *abortbut, *resbut, *cleanbut;
  190.  
  191.     if (obj = (Object *)DoSuperNew(cl, obj,
  192.         MUIA_HelpNode, "download",
  193.         MUIA_Window_Title, MSG_DL_TITLE,
  194.         MUIA_Window_ID, MAKE_ID('D','O','W','N'),
  195.         WindowContents, VGroup,
  196.             Child, list = NListviewObject,
  197.                 MUIA_NListview_NList, NListObject,
  198.                     InputListFrame,
  199.                     MUIA_Font, MUIV_Font_Tiny,
  200.                     MUIA_NList_Title, TRUE,
  201.                     MUIA_NList_Format, "BAR, BAR, BAR, BAR, BAR",
  202.                     MUIA_NList_DisplayHook, &downlistdispHook,
  203.                     MUIA_NList_DragSortable, TRUE,
  204.                     MUIA_CycleChain, 1,
  205.                 End,
  206.             End,
  207.             Child, info = TextObject,
  208.                 TextFrame,
  209.                 MUIA_Background, MUII_TextBack,
  210.                 MUIA_Text_PreParse, "\33c",
  211.             End,
  212.             Child, HGroup,
  213.                 Child, HGroup,
  214.                     Child, playbut = SimpleButton(MSG_DL_PLAY_GAD),
  215.                     MUIA_ShortHelp, MSG_PLAY_HELP,
  216.                 End,
  217.                 Child, HSpace(4),
  218.                 Child, HGroup,
  219.                     Child, abortbut = SimpleButton(MSG_DL_ABORT_GAD),
  220.                     MUIA_ShortHelp, MSG_ABORT_HELP,
  221.                 End,
  222.                 Child, HGroup,
  223.                     Child, resbut = SimpleButton(MSG_DL_RESUME_GAD),
  224.                     MUIA_ShortHelp, MSG_RESUME_HELP,
  225.                 End,
  226.                 Child, HSpace(4),
  227.                 Child, HGroup,
  228.                     Child, cleanbut = SimpleButton(MSG_DL_CLEANUP_GAD),
  229.                     MUIA_ShortHelp, MSG_CLEANUP_HELP,
  230.                 End,
  231.             End,
  232.         End,
  233.         TAG_MORE, msg->ops_AttrList))
  234.     {
  235.         data = INST_DATA(cl,obj);
  236.         data->list = list;
  237.         data->info = info;
  238.         data->BT_Resume = resbut;
  239.  
  240.         data->ihnode.ihn_Object = obj;
  241.         data->ihnode.ihn_Millis = 1000;
  242.         data->ihnode.ihn_Method = DL_CPS;
  243.         data->ihnode.ihn_Flags  = MUIIHNF_TIMER;
  244.  
  245.         data->waitnode.ihn_Object = obj;
  246.         data->waitnode.ihn_Millis = prf->QueueDelay*1000;    /* Poll interval in milliseconds */
  247.         data->waitnode.ihn_Method = DL_POLLWAIT;
  248.         data->waitnode.ihn_Flags  = MUIIHNF_TIMER;
  249.  
  250.         data->watchnode.ihn_Object = obj;
  251.         data->watchnode.ihn_Millis = 10000;
  252.         data->watchnode.ihn_Method = DL_WATCHER;
  253.         data->watchnode.ihn_Flags  = MUIIHNF_TIMER;
  254.  
  255.         DoMethod(playbut,  MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, DL_PLAY, PRFE_PLAYMP3 );
  256.         DoMethod(resbut,   MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_RESUME );
  257.         DoMethod(abortbut, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_ABORT  );
  258.         DoMethod(cleanbut, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_CLEANUP);
  259.  
  260.         DoMethod(list, MUIM_Notify, MUIA_NList_EntryClick,  MUIV_EveryTime, obj, 1, DL_INFO);
  261.         DoMethod(list, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 2, DL_PLAY, PRFE_DL_DCLICK);
  262.  
  263.         DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, gui->iconpanel, 1, PANEL_CLOSEDL);
  264.  
  265.         return((ULONG)obj);
  266.     }
  267.  
  268.     return(0);
  269. }
  270.  
  271.  
  272. void dl_addq(song s)
  273. /* This is the initial function, called from resultview.c */
  274. {
  275.     songtrans sd;
  276.  
  277. #ifdef AMSTER_DEBUG
  278. gui_debug("dl_addq()");
  279. #endif
  280.     sd = malloc(sizeof(_songtrans));
  281.     if (!sd) return;
  282.     memset(sd, 0, sizeof(_songtrans));
  283.  
  284.     sd->song = nap_songdup(s);
  285.     if (!sd->song) {
  286.         free(sd);
  287.         return;
  288.     }
  289.  
  290.     sd->size = s->size;
  291.     sd->mynick = prf->user;
  292.     sd->type = TYPE_DOWNLOAD_OUT;
  293.     sd->reqtime = GetDateStamp();
  294.  
  295.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) {
  296.         sd->state = DLS_QUEUE;
  297.         QueueCount++;
  298.     }
  299.     else {
  300.         sprintf(nap_buf, "\"%s\" \"%s\"", s->user, s->title);
  301.         nap_send(NAPC_FILEINFOREQ);
  302.         sd->state = DLS_PREP;
  303.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  304.     }
  305.  
  306. #ifdef AMSTER_DEBUG
  307. gui_debugf("new download count: %d (concurrent setting: %d), queue count: %d", dl_count, prf->DownloadQueueLimit, QueueCount);
  308. #endif
  309.  
  310.     DoMethod(gui->dwin, DL_ADD, sd);
  311. }
  312.  
  313.  
  314. void dl_startq(struct TransferData *data, char *title, char *user, u_long ip, int port)
  315. /* This is the initial function, when the actual download is about to
  316.    take place (acknowledged by server) - called from napster.c (via DL_START) */
  317. {
  318.     u_long tmp;
  319.     songtrans sd;
  320.     long i;
  321.  
  322. #ifdef AMSTER_DEBUG
  323. gui_debugf("port : %d (file: %s, user: %s)", port, title, user);
  324. #endif
  325.  
  326.     for (i=0; ; i++) {
  327.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  328.         if (!tmp) return;
  329.         sd = (songtrans)tmp;
  330.         if ((sd->state == DLS_PREP || sd->state == DLS_WAIT || sd->state == DLS_QUEUE) && stricmp(user,sd->song->user)==0 && strcmp(title,sd->song->title)==0) break;
  331.     }
  332.  
  333.     if (sd->t) {
  334. #ifdef AMSTER_DEBUG
  335. gui_debug("this shouldn't happen (1)!");
  336. #endif
  337.         return;    /* Already in a thread */
  338.     }
  339.  
  340.     if (!dl_filename(sd)) {
  341.         if (sd->state == DLS_WAIT) DoMethod(gui->dwin, DL_REMWAITING, sd);
  342.         else if (sd->state == DLS_QUEUE) QueueCount--;
  343.         else {
  344.             sd->state = DLS_ABORT;
  345.             DoMethod(gui->dwin, DL_COUNTDECREMENT);
  346.         }
  347.         sd->state = DLS_ABORT;
  348.         DoMethod(gui->dwin, DL_UPDATE, sd);
  349.         return;
  350.     }
  351.  
  352.     prf_event(PRFE_DLSTART, sd->fname);
  353.  
  354. #ifdef AMSTER_DEBUG
  355. gui_debugf("ack [%s] - IP: %ld", title, ip);
  356. #endif
  357.  
  358.     sd->song->ip = ip;    /* In case of browse result, the IP isn't already there */
  359.  
  360.     sd->ip = ip;
  361.     sd->port = port;
  362.     sd->s = -1;
  363.     sd->oldsize = sd->cur;
  364.  
  365.     if (sd->state == DLS_WAIT) DoMethod(gui->dwin, DL_REMWAITING, sd);
  366.  
  367. /*
  368.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) return;
  369. */
  370.     /* Simply do not spawn the thread if the download is queued. This shouldn't
  371.        be possible though. */
  372.  
  373.     if (sd->state == DLS_QUEUE) {
  374.         QueueCount--;
  375.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  376.     }
  377.     else if (sd->state == DLS_WAIT) {
  378.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  379.     }
  380.  
  381.     sd->state = DLS_PREP;
  382.  
  383.     if (port == 0) {
  384.         /* The other user is behind firewall, so we'll wait for him to make the connection.
  385.            After that we spawn the thread. */
  386.         sd->type = TYPE_DOWNLOAD_IN;
  387.         gui_debugf("User %s is behind firewall, so we send alternate download request.", user);
  388.         sprintf(nap_buf, "%s \"%s\"", user, title);
  389.         nap_send(NAPC_ALTDLREQ);
  390.     }
  391.     else {
  392.         sd->t = th_spawn(dl_handlemsg, "Amster downloader", &dl_sucker, prf->DownloadTaskPri, sd);
  393.         if (!sd->t) {
  394.             sd->state = DLS_ERROR;
  395.             DoMethod(gui->dwin, DL_UPDATE, sd);
  396.         }
  397.     }
  398. }
  399.  
  400.  
  401. void dl_startqi(struct TransferData *data, long key)
  402. /* This is the initial function, when a firewalled download is about to
  403.    take place (acknowledged by other client) - called from napster.c (via DL_STARTINCOMING) */
  404. {
  405.     u_long tmp;
  406.     long len;
  407.     long s;
  408.     songtrans sd;
  409.     long i;
  410.     char *user, *title;
  411.     int size;
  412.     char *buffer;
  413.     char *test;
  414.  
  415.     buffer = malloc(512);
  416.     test = buffer;
  417.     /* Add error handling */
  418.  
  419. len = sprintf(buffer, "%ld", 0);
  420. gui_debugf("result: %d, buffer(addr: %x/len: %d): %s", len, &buffer, strlen(buffer), buffer);
  421.  
  422.     s = ObtainSocket(key, AF_INET, SOCK_STREAM, 0);
  423.  
  424. gui_debugf("dl_startqi (socket: %ld)", s);
  425.  
  426.     /* Make sure we have bsdsocket.library open etc. */
  427.  
  428.     len = recv(s, buffer, 511, 0);
  429.     if (len < 1) {
  430.         /* Clean up here! */
  431. gui_debugf("net error: %ld/%ld", len, Errno());
  432.         CloseSocket(s);
  433.         free(buffer);
  434.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  435.         return;
  436.     }
  437.     buffer[len] = '\0';
  438.  
  439. gui_debugf("from listener thread (len: %ld): %s\n", len, buffer);
  440.  
  441.     user = nap_token(&buffer);
  442.     title = nap_token(&buffer);
  443.     size = nap_ltoken(&buffer);
  444.  
  445. gui_debugf("parsed: %s/%s/%ld", user, title, size);
  446.  
  447. free(buffer);
  448. buffer=malloc(512);
  449.  
  450. /*
  451. len = sprintf(buffer, "%ld", 0);
  452. gui_debugf("result: %d, buffer(addr: %x/len: %d): %s", len, &buffer, strlen(buffer), buffer);
  453. */
  454.  
  455.     for (i=0; ; i++) {
  456.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  457.         if (!tmp) {
  458.             /* "INVALID REQUEST" etc. + cleanup here */
  459.             CloseSocket(s);
  460.             free(buffer);
  461.             return;
  462.         }
  463.         sd = (songtrans)tmp;
  464.         if ((sd->state == DLS_PREP) && stricmp(user,sd->song->user)==0 && strcmp(title,sd->song->title)==0) break;
  465.     }
  466.  
  467. gui_debugf("found! - cur: %ld", sd->cur);
  468.  
  469.     len = sprintf(buffer, "%ld", sd->cur);
  470. gui_debugf("result: %d, buffer(addr: %x/len: %d): %s", len, &buffer, strlen(buffer), buffer);
  471.  
  472.     len = send(s, buffer, strlen(buffer), 0);
  473.     if (len < 1) {
  474. gui_debugf("result: %d/%d", len, Errno());
  475.         CloseSocket(s);
  476.         free(buffer);
  477.         sd->state = DLS_ERROR;
  478.         sd->error = ERROR_UNKNOWN;
  479.         DoMethod(gui->dwin, DL_UPDATE, sd);
  480.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  481.         return;
  482.     }
  483.  
  484.     free(buffer);
  485.  
  486.     if (sd->t) {
  487. gui_debug("this shouldn't happen (1)!");
  488.         CloseSocket(s);
  489.         sd->state = DLS_ERROR;
  490.         DoMethod(gui->dwin, DL_UPDATE, sd);
  491.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  492.         return;    /* Already in a thread */
  493.     }
  494.  
  495.     /* Spawn thread etc. */
  496.  
  497.     sd->s = ReleaseSocket(s, GetUniqueID());
  498.     /* This is NOT the socket descriptor, but a key we can use to get it in the download thread */
  499.  
  500.     sd->t = th_spawn(dl_handlemsg, "Amster downloader (firewall)", &dl_sucker, prf->DownloadTaskPri, sd);
  501.     if (!sd->t) {
  502.         sd->state = DLS_ERROR;
  503.         DoMethod(gui->dwin, DL_UPDATE, sd);
  504.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  505.     }
  506. }
  507.  
  508.  
  509. void dl_checkqueue(struct TransferData *data)
  510. {
  511.     u_long tmp;
  512.     songtrans sd;
  513.     long i;
  514.  
  515.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) return;
  516.  
  517.     for (i=0; ; i++) {
  518.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  519.         if (!tmp) return;
  520.         sd = (songtrans)tmp;
  521.         if (sd->state == DLS_QUEUE) break;
  522.     }
  523.  
  524.     sd->reqtime = GetDateStamp();    /* Reset time to avoid watcher timeouts */
  525.  
  526.     sprintf(nap_buf, "\"%s\" \"%s\"", sd->song->user, sd->song->title);
  527.     nap_send(NAPC_FILEINFOREQ);
  528.     DoMethod(gui->dwin, DL_COUNTINCREMENT);
  529.     sd->state = DLS_PREP;
  530.     QueueCount--;
  531.  
  532. #ifdef AMSTER_DEBUG
  533. gui_debugf("queue spawn - new dl_count: %d/QueueCount: %d (%s)", dl_count, QueueCount, sd->song->title);
  534. #endif
  535.  
  536.     DoMethod(gui->dwin, DL_UPDATE, sd);
  537. }
  538.  
  539.  
  540. void DownloadRetry(struct TransferData *data, Object *obj, char *title, char *user, int limit)
  541. /* Called from napster.c when a file cannot be downloaded yet (busy) */
  542. {
  543.     u_long tmp;
  544.     songtrans sd;
  545.     long i;
  546.  
  547. #ifdef AMSTER_DEBUG
  548. gui_debugf("queue full (server rejection): %s", title);
  549. #endif
  550.     for (i=0; ; i++) {
  551.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  552.         if (!tmp) return;
  553.         sd = (songtrans)tmp;
  554.         if (sd->state == DLS_PREP && strcmp(sd->song->title, title) == 0 && stricmp(sd->song->user, user) == 0) break;
  555.     }
  556.  
  557.     if (sd->RetryCount >= prf->QueueRetries) return;    /* We've already given up */
  558.     else if (limit == 0) {
  559.         sd->state = DLS_ERROR;
  560.         sd->error = ERROR_TEASER;    /* Queue limit is 0 = file can't be downloaded by anyone */
  561.  
  562.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  563.     }
  564.     else {
  565.         sd->state = DLS_WAIT;
  566.         sd->ErrorCode = limit;
  567.         WaitingCount++;
  568.  
  569.         if (WaitingCount == 1) DoMethod(_app(obj), MUIM_Application_AddInputHandler, &data->waitnode);
  570.         /* This is the first waiting entry - add input handler */
  571.  
  572.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  573.         /* We consider this queued and let other downloads start */
  574.     }
  575.  
  576.     DoMethod(gui->dwin, DL_UPDATE, sd);
  577. }
  578.  
  579.  
  580. void PollWaiting(struct TransferData *data, Object *obj)
  581. {
  582.     u_long tmp;
  583.     songtrans sd;
  584.     long i;
  585.  
  586. #ifdef AMSTER_DEBUG
  587. gui_debug("PollWaiting()");
  588. #endif
  589.  
  590.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) {
  591. #ifdef AMSTER_DEBUG
  592. gui_debug("- cancelled due to local download count limit!");
  593. #endif
  594.         return;
  595.     }
  596.  
  597.     if (WaitingCount == 0) return;    /* Don't waste time! */
  598.  
  599.     for (i=0; ; i++) {
  600.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  601.         if (!tmp) return;
  602.         sd = (songtrans)tmp;
  603.         if (sd->state == DLS_WAIT && QueueCount == 0) {
  604.             if (sd->RetryCount < prf->QueueRetries) {
  605. #ifdef AMSTER_DEBUG
  606. gui_debugf("Repeating request for '%s'", sd->song->title);
  607. #endif
  608.                 sprintf(nap_buf,"\"%s\" \"%s\"", sd->song->user, sd->song->title);
  609.                 nap_send(NAPC_FILEINFOREQ);
  610.                 sd->RetryCount++;
  611.             }
  612.             else {    /* We give up! */
  613.                 sd->state = DLS_ERROR;
  614.                 sd->error = ERROR_BUSY;
  615.                 DoMethod(gui->dwin, DL_REMWAITING, sd);
  616.                 DoMethod(gui->dwin, DL_UPDATE, sd);
  617.             }
  618.         }
  619.     }
  620. }
  621.  
  622.  
  623. void dl_resume(struct TransferData *data)
  624. {
  625.     u_long item;
  626.     songtrans sd;
  627.  
  628. #ifdef AMSTER_DEBUG
  629. gui_debug("dl_resume()");
  630. #endif
  631.     DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &item);
  632.     if (!item) return;
  633.  
  634.     sd = (songtrans)item;
  635.     if (sd->state < DLS_ABORT) return;    /* Can't resume files that's already in progress */
  636. #ifdef AMSTER_DEBUG
  637. gui_debug("1");
  638. #endif
  639.     if (sd->t) return;
  640. #ifdef AMSTER_DEBUG
  641. gui_debug("2");
  642. #endif
  643.  
  644.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) {
  645.         sd->state = DLS_QUEUE;
  646.         QueueCount++;
  647.     }
  648.     else {
  649. #ifdef AMSTER_DEBUG
  650. gui_debug("3");
  651. #endif
  652.         sd->reqtime = GetDateStamp();
  653.         sprintf(nap_buf, "\"%s\" \"%s\"", sd->song->user, sd->song->title);
  654.         nap_send(NAPC_FILEINFOREQ);
  655.         sd->state = DLS_PREP;
  656.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  657.     }
  658.  
  659. #ifdef AMSTER_DEBUG
  660. gui_debugf("download count (resume): %d", dl_count);
  661. #endif
  662.  
  663.     set(data->BT_Resume, MUIA_Disabled, TRUE);
  664.     DoMethod(gui->dwin, DL_UPDATE, sd);
  665. }
  666.  
  667.  
  668. int dl_filename(songtrans sd)
  669. {
  670.     BPTR lock;
  671.     ALIGNED struct FileInfoBlock fib;
  672.     char md5[80] = "";
  673.     char *fname, temp[108];
  674.     long oldsize=0;
  675.     int way, reqrc;
  676.     char *expl, *choice;
  677.  
  678.     fname = malloc(512);
  679.     if (!fname) return 0;
  680.     strcpy(fname, prf->dlpath);
  681.     strncpy(temp, nap_strippath(sd->song->title), 107);
  682.     temp[107] = '\0';
  683.     AddPart(fname, temp, 511);
  684.  
  685.     if (prf->askfile) {
  686.         if (!dl_askfname(fname)) return 0;
  687.     }
  688.     else {    /* Needs optimization and elegance */
  689.         if (strlen(nap_strippath(sd->song->title)) > prf->NameLength) {
  690.             strcpy(fname+strlen(fname)-strlen(nap_strippath(sd->song->title))-4+prf->NameLength, fname+strlen(fname)-4);
  691.         }
  692.     }
  693.  
  694.     lock = Lock(fname, ACCESS_READ);
  695.     if (!lock) {
  696.         sd->fname = fname;
  697.         sd->cur = 0;
  698.         return(1);
  699.     }
  700.  
  701.     if (Examine(lock, &fib)) {
  702.         oldsize = fib.fib_Size;
  703.         sscanf(fib.fib_Comment, "md5: %[^;]", &md5);
  704.         if (md5[0] == 0 && oldsize >= 300032) strcpy(md5, chkmd5_fromlock(lock));
  705.             else UnLock(lock);
  706.     }
  707.     else UnLock(lock);
  708.  
  709.     if (sd->size <= oldsize) {
  710.         way = 1;
  711.         expl = (char *)MSG_DL_RESBIGSIZE;
  712.         choice = (char *)MSG_DL_OVERCANCEL_GAD;
  713.     }
  714.     else if (md5[0] == 0) {
  715.         way = 0;
  716.         expl = (char *)MSG_DL_NOMD5INFO;
  717.         choice = (char *)MSG_DL_RESUMECANCEL_GAD;
  718.     }
  719.     else if (strcmp(md5,sd->song->md5)!=0) {
  720.         way = 1;
  721.         expl = (char *)MSG_DL_NOMATCH;
  722.         choice = (char *)MSG_DL_OVERCANCEL_GAD;
  723.     }
  724.     else {
  725.         way = 2;
  726.         expl = (char *)MSG_DL_RESMATCH;
  727.         choice = (char *)MSG_DL_RESUMEOVER_GAD;
  728.     }
  729.  
  730.     reqrc = MUI_Request(gui->app, gui->win, 0L,
  731.                         (char *)MSG_DL_RESUME_INFO,
  732.                         choice,
  733.                         (char *)MSG_DL_RESUME_MSG,
  734.                         fname,
  735.                         oldsize,
  736.                         sd->size,
  737.                         md5[0]==0 ? (char*)MSG_DL_NOMD5 : md5,
  738.                         sd->song->md5,
  739.                         expl);
  740.  
  741.     if (reqrc == 0) return(0);
  742.  
  743.     if ((way==0 || way==2) && reqrc == 1) {
  744.         sd->fname = fname;
  745.         sd->cur = oldsize;
  746.         return(1);
  747.     }
  748.  
  749.     if (reqrc == 2) {
  750.         if (dl_askfname(fname)) {
  751.             sd->fname = fname;
  752.             sd->cur = 0;
  753.             return(1);
  754.         }
  755.         else return(0);
  756.     }
  757.  
  758.     sd->cur = 0;
  759.     sd->fname = fname;
  760.     return(1);
  761. }
  762.  
  763.  
  764. char *chkmd5_fromlock(BPTR lock)
  765. {
  766.     APTR buffer;
  767.     BPTR fh;
  768.     int len;
  769.     char md5[33] = "";
  770.  
  771.     md5_state_t state;
  772.     md5_byte_t digest[16];
  773.     int di;
  774.  
  775.     md5_init(&state);
  776.  
  777.     if (fh = OpenFromLock(lock)) {
  778.         if (buffer = AllocMem(300032, MEMF_ANY)) {
  779.  
  780.             len = Read(fh, buffer, 300032);
  781.             if (len > 0) {
  782.                 md5_append(&state, (const md5_byte_t *)buffer, len);
  783.                 md5_finish(&state, digest);
  784.                 for (di = 0; di < 16; ++di)
  785.                     sprintf(md5+di*2, "%02x", digest[di]);
  786.             }
  787.             FreeMem(buffer, 300032);
  788.         }
  789.         Close(fh);
  790.     }
  791.     else UnLock(lock);
  792.  
  793.     return md5;
  794. }
  795.  
  796.  
  797. int dl_askfname(char *fname)
  798. {
  799.     struct FileRequester *freq;
  800.     u_long win;
  801.  
  802.     freq = AllocAslRequestTags(ASL_FileRequest, TAG_DONE);
  803.     if (!freq) return(0);
  804.  
  805.     get(gui->dwin, MUIA_Window_Window, &win);
  806.     if (AslRequestTags(freq,
  807.                     ASLFR_Window, win,
  808.                     ASLFR_TitleText, MSG_DL_SELECTFILE,
  809.                     ASLFR_InitialFile, nap_strippath(fname),
  810.                     ASLFR_InitialDrawer, prf->dlpath,
  811.                     ASLFR_DoSaveMode, TRUE,
  812.                     TAG_DONE)) {
  813.         strcpy(fname, freq->fr_Drawer);
  814.         AddPart(fname, freq->fr_File, 511);
  815.         FreeAslRequest(freq);
  816.         return(1);
  817.     }
  818.     else {
  819.         FreeAslRequest(freq);
  820.         return(0);
  821.     }
  822. }
  823.  
  824.  
  825. void dl_handlemsg(thread t, int com, APTR data)
  826. /* This is the function that receives messages from the download
  827.    thread. It's called when something needs to be done in the
  828.    main thread (i.e. updating the window). */
  829. {
  830.     songtrans sd=(songtrans)t->data;
  831.  
  832.     switch (com) {
  833.         case THC_STARTUP:
  834. #ifdef AMSTER_DEBUG
  835. gui_debug("thread start");
  836. #endif
  837.             sd->ts = 1;
  838.             nap_sendbuf(NAPC_DLINC, "");
  839.             break;
  840.         case THC_EXIT:
  841. #ifdef AMSTER_DEBUG
  842. gui_debugf("thread exit = %ld, file: %s", data, sd->fname);
  843. #endif
  844.             sd->error = (int)data;
  845.             TransferHandleError(sd);
  846.             sd->ts = 0;
  847.             sd->t = NULL;
  848.             DoMethod(gui->dwin, DL_COUNTDECREMENT);
  849.             nap_sendbuf(NAPC_DLCOMPLETE, "");
  850.             if (sd->state == DLS_FIN && (prf->AutoCleanup == 1 || prf->AutoCleanup == 3)) {
  851.                 DoMethod(gui->dwin, DL_CLEANUP_SINGLE, sd);
  852.                 break;
  853.             }
  854.             break;
  855.         case DLC_STATE:
  856.             sd->state = (int)data;
  857.         case DLC_UPDATE:
  858.             DoMethod(gui->dwin, DL_UPDATE, sd);
  859.             break;
  860.         case DLC_ADDSHARE:
  861.             DoMethod(gui->shwin, SHARE_ADDFILE, sd->song, sd->fname);
  862.             break;
  863. #ifdef AMSTER_DEBUG
  864.         default:
  865. gui_debugf("thread c: %d, d: %ld", com, data);
  866. #endif
  867.     }
  868. }
  869.  
  870.  
  871. void dl_cps(struct TransferData *data)
  872. {
  873.     songtrans sd;
  874.     u_long item;
  875.     int i;
  876.  
  877.     for (i=0; ; i++) {
  878.         DoMethod(data->list, MUIM_NList_GetEntry, i, &item);
  879.         if (!item) return;
  880.         sd = (songtrans)item;
  881.         if (sd->state == DLS_DOWN) {
  882.             CalculateCps(sd);
  883.             DoMethod(gui->dwin, DL_UPDATE, sd);
  884.         }
  885.     }
  886. }
  887.  
  888.  
  889. /* thread code */
  890.  
  891. THREAD(dl_sucker)
  892. {
  893.     thread t;
  894.     songtrans sd;
  895.     struct Library *DOSBase;
  896.     struct Library *SocketBase;
  897.     char *buffer;
  898.     long tmp;
  899.     long s;
  900.     thmsg m;
  901.     char cmnt[80];
  902.     int count;
  903.  
  904.     t = thr_init();
  905.     if (!t) return;
  906.     sd = t->data;
  907.  
  908. #ifdef AMSTER_DEBUG
  909. gui_debug("blah-1");
  910. #endif
  911.     if (!InitTransferThread(t, sd)) return;
  912. #ifdef AMSTER_DEBUG
  913. gui_debug("blah-2");
  914. #endif
  915.     sd->RetryCount = 0;
  916.     s = sd->s;
  917.     buffer = sd->buffer;
  918.     DOSBase = sd->DOSBase;
  919.     SocketBase = sd->SocketBase;
  920.  
  921.     while (1) {
  922.         u_long sigs;
  923.  
  924.         sigs = Wait(sd->nsigm | sd->msigm);
  925.         if (sigs&(sd->msigm)) {
  926.             m = (thmsg)GetMsg(t->port);
  927.             if (m) {
  928.                 if (m->com == THC_EXIT) {
  929.                     sd->state = DLS_ABORT;
  930.                     thr_message(t, DLC_UPDATE, 0);
  931.                     ExitTransferThread(sd, 0);
  932.                     return;
  933.                 }
  934.                 if (!m->isreply) {
  935.                     m->isreply=1;
  936.                     ReplyMsg((struct Message *)m);
  937.                 }
  938.             }
  939.         }
  940.  
  941.         if (sigs&(sd->nsigm)) {
  942.             FD_ZERO(&sd->fds);
  943.             FD_SET(s,&sd->fds);
  944.             sd->tv.tv_sec = 0;
  945.             sd->tv.tv_usec = 0;
  946.  
  947.             switch (sd->state) {
  948.                 case DLS_CON:
  949.                     if (WaitSelect(s+1,NULL,&sd->fds,NULL,&sd->tv,0) != 1) break;
  950.                     {
  951.                         sd->state = DLS_REQ;
  952.                         tmp = 0;
  953.                         IoctlSocket(s, FIONBIO, (char*)&tmp);
  954.                         /* Disable non-blocking I/O to the socket */
  955.                     }
  956.  
  957.                 case DLS_REQ:
  958.                     if (WaitSelect(s+1,&sd->fds,NULL,NULL,&sd->tv,0) != 1) break;
  959.                     {
  960.                         tmp = recv(s, buffer, 1, 0);
  961.                         if (tmp != 1) {
  962.                             sd->ErrorCode = Errno();
  963.                             ExitTransferThread(sd, ERROR_NET);
  964.                             return;
  965.                         }
  966.                         if (buffer[0] != '1') {
  967.                             ExitTransferThread(sd, 29);
  968.                             return;
  969.                         }
  970.                         thr_message(t, DLC_UPDATE, 0);
  971.                         send(s, "GET", 3, 0);
  972.                         sprintf(buffer, "%s \"%s\" %ld", sd->mynick, sd->song->title, sd->cur);
  973.                         send(s, buffer, strlen(buffer), 0);
  974.  
  975.                         tmp = recv(s, buffer, 32, 0);
  976.                         if (tmp < 1) {
  977.                             sd->ErrorCode = Errno();
  978.                             ExitTransferThread(sd, ERROR_NET);
  979.                             return;
  980.                         }
  981.                         buffer[tmp] = '\0';
  982.  
  983.                         if (atoi(buffer) != sd->size) {
  984.                             if (strcmp(buffer, "FILE NOT FOUND") == 0 || strcmp(buffer, "FILE NOT SHARED") == 0) {
  985.                                 ExitTransferThread(sd, ERROR_NOTFOUND);
  986.                                 return;
  987.                             }
  988.                             else if (strcmp(buffer, "INVALID REQUEST") == 0) {
  989.                                 ExitTransferThread(sd, ERROR_INVALIDREQUEST);
  990.                                 return;
  991.                             }
  992. #ifdef AMSTER_DEBUG
  993. gui_debugf("error (please report this): %s", buffer);
  994. #endif
  995.                             ExitTransferThread(sd, ERROR_UNKNOWN);
  996.                             return;
  997.                         }
  998.                         sd->state = DLS_INIT;
  999.                     }
  1000.  
  1001.                 case DLS_INIT:
  1002.                     sd->f = Open(sd->fname, MODE_READWRITE);
  1003.                     if (!sd->f) {
  1004.                         sd->ErrorCode = IoErr();
  1005.                         ExitTransferThread(sd, ERROR_FILEOPEN);
  1006.                         return;
  1007.                     }
  1008.                     sprintf(cmnt, "md5:%s; size:%ld; user:%s", sd->song->md5, sd->size, sd->song->user);
  1009.                     SetComment(sd->fname, cmnt);
  1010.                     Seek(sd->f, sd->cur, OFFSET_BEGINNING);
  1011.                     sd->state = DLS_DOWN;
  1012.                     sd->starttime = GetDateStamp();
  1013.                     sd->resumestart = sd->cur;
  1014.                     thr_message(t, DLC_UPDATE, 0);
  1015.  
  1016.                 case DLS_DOWN:
  1017.                     if (WaitSelect(s+1,&sd->fds,NULL,NULL,&sd->tv,0) != 1) break;
  1018.                     while (sd->cur < sd->size) {
  1019.  
  1020.                         tmp = recv(s, buffer, 4096, 0);
  1021.                         if (tmp <= 0) {
  1022.                             sd->ErrorCode = Errno();
  1023.                             /* If recv() returns 0 the error is EINPROGRESS
  1024.                                (Net error 36) - I don't know what's causing
  1025.                                this, but if not failing the thread will busy-loop */
  1026.                             ExitTransferThread(sd, ERROR_NET);
  1027.                             return;
  1028.                         }
  1029.                         SetIoErr(0L);    /* FWrite doesn't clear IoErr() due to a bug */
  1030.                         count = FWrite(sd->f, buffer, 1, tmp);
  1031.                         sd->cur += count;
  1032.                         if (count != tmp) {
  1033.                             sd->ErrorCode = IoErr();
  1034.                             ExitTransferThread(sd, ERROR_FILEWRITE);
  1035.                             return;
  1036.                         }
  1037.  
  1038.                         m = (thmsg)GetMsg(t->port);
  1039.                         if (m) {
  1040.                             if (m->com == THC_EXIT) {
  1041.                                 thr_message(t, DLC_STATE, (APTR)DLS_ABORT);
  1042.                                 ExitTransferThread(sd, 0);
  1043.                                 return;
  1044.                             }
  1045.                             if (!m->isreply) {
  1046.                                 m->isreply = 1;
  1047.                                 ReplyMsg((struct Message *)m);
  1048.                             }
  1049.                         }
  1050.  
  1051.                         FD_ZERO(&sd->fds);
  1052.                         FD_SET(s,&sd->fds);
  1053.                     }
  1054.  
  1055.                     sd->state = DLS_FIN;
  1056.                     thr_message(t, DLC_UPDATE, 0);
  1057.  
  1058.                     if ((strlen(sd->song->user)+strlen(sd->host)+9) < 80) sprintf(cmnt, "from %s (@%s)", sd->song->user, sd->host);
  1059.                     else sprintf(cmnt, "from %s (@%s)", sd->song->user, Inet_NtoA(sd->song->ip));
  1060.                     /* If the hostname is too long for the file comment, we fall back to the IP */
  1061.                     SetComment(sd->fname, cmnt);
  1062.  
  1063.                     if (prf->autoadd) thr_message(t, DLC_ADDSHARE, 0);
  1064.  
  1065.                     ExitTransferThread(sd, 0);
  1066.                     return;
  1067.  
  1068.             }
  1069.         }
  1070.     }
  1071.  
  1072.     ExitTransferThread(sd, 0);
  1073. }
  1074.